/*
 *  main.c
 *  
 *
 *  Created by Philip Asare on 12/6/10.
 *  Copyright 2010. All rights reserved.
 *
 *
 * monome sketch-it base station 
 *
 * communication
 * -------------
 * to controllers --> wireless (RT-Link)
 * to console display --> UART
 *
 */


#include <nrk.h>
#include <include.h>
#include <ulib.h>
#include <stdio.h>
#include <avr/sleep.h>
#include <hal.h>
#include <bmac.h>
#include <nrk_error.h>

/* controller tx slots --> base station rx slots */
#define C1_ADDR 0x0001
#define C2_ADDR 0x0002
#define C3_ADDR 0x0003
#define C4_ADDR 0x0004
#define ALL_ADDR 0xffff
#define MY_ADDR 0x000f


/* game states and controller tracking */
#define MAX_NUM_PLAYERS 4

#define VERY_EASY 90
#define EASY 60
#define MEDIUM 45
#define HARD 30

uint8_t state, num_players;
uint16_t player_addr[MAX_NUM_PLAYERS];

uint8_t num_plays_current_game = 0;

nrk_time_t game_time, start_time, current_time;

char player_initials[4][3];
char initials[3];
uint8_t initials_set[4];

uint8_t player_grids[4][8];
uint8_t player_grids_turned_in[4];
uint8_t grid[8] = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

#define SYNC 0
#define SELECT 1
#define PREGAME 2
#define GAMEPLAY 3
#define PLAYERASSIGN 4
#define GAMEOVER_CALC 5
#define GAMEOVER_CHOICE 6
#define CHANGE_STATE 7

#define COMPETITIVE 0
#define COOPERATIVE 1

/* monome helper functions */

#define type(x) sizeof(x)

typedef struct{
	uint8_t src;
	uint8_t dest;
	uint8_t player;
} monome_comm_sync_pkt;

typedef struct{
	uint8_t src;
	uint8_t dest;
	uint8_t state;
} monome_comm_state_pkt;

typedef struct{
	uint8_t src;
	uint8_t dest;
	uint8_t players;
	uint8_t game_type;
} monome_comm_select_pkt;

monome_comm_sync_pkt sync_pkt;
monome_comm_select_pkt select_pkt;
monome_comm_state_pkt state_pkt;

uint8_t tx_len;

/* task setup */

NRK_STK Stack1[NRK_APP_STACKSIZE];
nrk_task_type TaskOne;
void Task1(void);

NRK_STK TimerStack[NRK_APP_STACKSIZE];
nrk_task_type TimerTask;
void timer_task(void);

inline void monome_comm_init();
inline void monome_setup_system();
inline void monome_add_controller(uint8_t slot);
inline void monome_validate_game();
inline void monome_comm_send_state(uint8_t state);
void monome_set_player_initials(uint8_t slot);
void nrk_create_taskset();

uint8_t tx_buf[RF_MAX_PAYLOAD_SIZE];
uint8_t rx_buf[RF_MAX_PAYLOAD_SIZE];

int
main ()
{
	nrk_setup_ports();
	nrk_setup_uart(UART_BAUDRATE_115K2);
	
	printf( "Starting up...\r\n" );
	
	
	nrk_init();
	
	nrk_led_clr(0);
	nrk_led_clr(1);
	nrk_led_clr(2);
	nrk_led_clr(3);
	
	nrk_time_set(0,0);
	
	
	bmac_task_config();
	
	nrk_create_taskset ();
	
	nrk_start();
	
	return 0;
}

void timer_task(){
	uint8_t i;
	game_time.secs = 0;
	game_time.nano_secs = 0;
	while(!bmac_started())  nrk_wait_until_next_period(); 
	while(1){
		printf("state: %d\r\n", state);
		if(state == GAMEPLAY){
			if(num_plays_current_game < 2){
				game_time.secs = VERY_EASY;
			}
			if(num_plays_current_game < 4){
				game_time.secs = EASY;
			}
			if(num_plays_current_game < 6){
				game_time.secs = MEDIUM;
			}
			else{
				game_time.secs = HARD;
			}
			nrk_led_clr(RED_LED);
			nrk_led_set(GREEN_LED);
			nrk_time_get(&current_time);
			while((current_time.secs - start_time.secs) < game_time.secs){
				nrk_wait_until_next_period();
				nrk_time_get(&current_time);
			}
			if(state == GAMEPLAY){
				state = GAMEOVER_CALC;
				tx_buf[2] = GAMEOVER_CALC;
				tx_buf[1] = ALL_ADDR%256;
				for(i = 0; i <select_pkt.players; i++){
					tx_buf[1] = player_addr[i]%256;
					bmac_addr_decode_dest_mac(player_addr[i]);
					while(bmac_tx_pkt( tx_buf, tx_len)!=NRK_OK);
				}
				printf( "Gameover\r\n" );
			}
			nrk_led_clr(GREEN_LED);
			nrk_led_set(RED_LED);
		}
		nrk_wait_until_next_period();
	}
	
	
}

void Task1()
{
	//uint8_t j, i;
	uint8_t *local_rx_buf;
	uint8_t length, i;
	int8_t rssi;
	
	monome_setup_system();
	monome_comm_init();
	
	while(!bmac_started())  nrk_wait_until_next_period(); 
	/*tx_buf[1] = ALL_ADDR;
	 tx_buf[2] = SYNC;
	 bmac_tx_pkt(tx_buf, 3);*/
	while(1){
		
		nrk_led_set(ORANGE_LED);
		if(bmac_rx_pkt_ready() == 0 )
		{
			bmac_wait_until_rx_pkt();
			nrk_led_set(BLUE_LED);
			local_rx_buf=bmac_rx_pkt_get(&length, &rssi);
			printf( "Got Packet from %d length %d\r\n", local_rx_buf[0] ,length );
			if((state == SYNC) || (state == SELECT)){
				if(length == type(monome_comm_sync_pkt)){
					monome_add_controller(local_rx_buf[0]);
				}
				
				else if(state == SELECT && (length == type(monome_comm_select_pkt))){
					select_pkt.players = local_rx_buf[2];
					select_pkt.game_type = local_rx_buf[3];
					monome_validate_game();
				}
				else ;
				
			}
			
			else if(length == 5){
				initials[0] = local_rx_buf[2];
				initials[1] = local_rx_buf[3];
				initials[2] = local_rx_buf[4];
				monome_set_player_initials(local_rx_buf[0]);
				printf("player %d initials: %s\r\n", local_rx_buf[0], initials);
			}
			
			else if(state == GAMEPLAY && length == 10){
				memcpy(&grid, &local_rx_buf[3], 8);
				if(local_rx_buf[0] == player_addr[0]){
					memcpy(&player_grids[0], &grid, 8);
				}
				else if(local_rx_buf[0] == player_addr[1]){
					memcpy(&player_grids[1], &grid, 8);
				}
				else if(local_rx_buf[0] == player_addr[2]){
					memcpy(&player_grids[2], &grid, 8);
				}
				else if(local_rx_buf[0] == player_addr[3]){
					memcpy(&player_grids[3], &grid, 8);
				}
				else ;
				
				tx_buf[2] = GAMEOVER_CALC;
				tx_buf[1] = ALL_ADDR%256;
				for(i = 0; i <select_pkt.players; i++){
					tx_buf[1] = player_addr[i]%256;
					bmac_addr_decode_dest_mac(player_addr[i]);
					while(bmac_tx_pkt( tx_buf, tx_len)!=NRK_OK);
				}
				state = GAMEOVER_CALC;
				printf( "Gameover " );
				printf("And the winner is %d\r\n", local_rx_buf[0]);
				
				
			}
			
			else if(state == GAMEOVER_CALC && length == 10){
				memcpy(&grid, &local_rx_buf[3], 8);
				if(local_rx_buf[0] == player_addr[0]){
					memcpy(&player_grids[0], &grid, 8);
					player_grids_turned_in[0] = 1;
				}
				else if(local_rx_buf[0] == player_addr[1]){
					memcpy(&player_grids[1], &grid, 8);
					player_grids_turned_in[1] = 1;
				}
				else if(local_rx_buf[0] == player_addr[2]){
					memcpy(&player_grids[2], &grid, 8);
					player_grids_turned_in[2] = 1;
				}
				else if(local_rx_buf[0] == player_addr[3]){
					memcpy(&player_grids[3], &grid, 8);
					player_grids_turned_in[3] = 1;
				}
				int i, check;
				check = 0;
				for(i = 0; i<select_pkt.players; i++){
					check+=player_grids_turned_in[i];
				}
				if(check == select_pkt.players){
					//calculate and display scores
					monome_comm_send_state(GAMEOVER_CHOICE);
					state = GAMEOVER_CHOICE;
					printf("all players turned in grids\r\n");
				}
			}
			
			
			else if(state == GAMEOVER_CHOICE){
				if(length == 3){
					if(local_rx_buf[2] == 0){
						state = GAMEPLAY;
						tx_buf[2] = GAMEPLAY;
						tx_buf[3] = 1;
						while(bmac_tx_pkt(tx_buf, 4)!=NRK_OK);
						for(i = 0; i <select_pkt.players; i++){
							tx_buf[1] = player_addr[i]%256;
							bmac_addr_decode_dest_mac(player_addr[i]);
							while(bmac_tx_pkt( tx_buf, tx_len)!=NRK_OK);
						}
						num_plays_current_game++;
						printf("play again!\r\n");
						nrk_time_get(&start_time);
					}
					else if(local_rx_buf[2] == 1){
						state = SELECT;
						tx_buf[1] = ALL_ADDR%256;
						bmac_addr_decode_dest_mac(ALL_ADDR);
						tx_buf[2] = SELECT;
						while(bmac_tx_pkt(tx_buf, 3)!=NRK_OK);
						num_plays_current_game = 0;
						printf("select another game\r\n");
					}
					else ;
				}				
			}
			
			bmac_rx_pkt_release();
			nrk_led_clr(BLUE_LED);
			
		}
		nrk_led_clr(ORANGE_LED);
		nrk_wait_until_next_period();
		
	}
	
}


void
nrk_create_taskset()
{
	
	
	TaskOne.task = Task1;
	TaskOne.Ptos = (void *) &Stack1[NRK_APP_STACKSIZE-1];
	TaskOne.Pbos = (void *) &Stack1[0];
	TaskOne.prio = 2;
	TaskOne.FirstActivation = TRUE;
	TaskOne.Type = BASIC_TASK;
	TaskOne.SchType = PREEMPTIVE;
	TaskOne.period.secs = 0;
	TaskOne.period.nano_secs = 250*NANOS_PER_MS;
	TaskOne.cpu_reserve.secs = 0;
	TaskOne.cpu_reserve.nano_secs = 170*NANOS_PER_MS;
	TaskOne.offset.secs = 0;
	TaskOne.offset.nano_secs= 0;
	nrk_activate_task (&TaskOne);
	
	TimerTask.task = timer_task;
	TimerTask.Ptos = (void *) &TimerStack[NRK_APP_STACKSIZE-1];
	TimerTask.Pbos = (void *) &TimerStack[0];
	TimerTask.prio = 3;
	TimerTask.FirstActivation = TRUE;
	TimerTask.Type = BASIC_TASK;
	TimerTask.SchType = PREEMPTIVE;
	TimerTask.period.secs = 0;
	TimerTask.period.nano_secs = 250*NANOS_PER_MS;
	TimerTask.cpu_reserve.secs = 0;
	TimerTask.cpu_reserve.nano_secs = 75*NANOS_PER_MS;
	TimerTask.offset.secs = 0;
	TimerTask.offset.nano_secs= 0;
	nrk_activate_task (&TimerTask);
	
	
	printf ("Create done\r\n");
}

inline void monome_comm_init(){
	bmac_init (17);
	/* rx schedules */
	bmac_rx_pkt_set_buffer(rx_buf, RF_MAX_PAYLOAD_SIZE);
	bmac_set_cca_thresh(-24);
	bmac_addr_decode_set_my_mac(MY_ADDR);
	bmac_addr_decode_enable();
    bmac_auto_ack_enable();
	
}

inline void monome_setup_system(){
	int i, j;
	state = SYNC;
	num_players = 0;
	tx_buf[0] = MY_ADDR%256;
	state_pkt.src = MY_ADDR%256;
	state_pkt.dest = ALL_ADDR%256;
	for(i = 0; i < MAX_NUM_PLAYERS; i++){
		player_addr[i] = 0xf0;
		initials_set[i] = 0;
		player_grids_turned_in[i] = 0;
		for(j = 0; j< 3; j++){
			player_initials[i][j] = ' ';
		}
		for(j = 0; j<8; j++){
			player_grids[i][j] = 0x08;
		}
	}
	
	
}

inline void monome_add_controller(uint8_t addr){
	int i, already_player;
	already_player = 0;
	for(i = 0; i<num_players; i++){
		if(addr == player_addr[i]){
			already_player = 1;
		}
	}
	if(already_player == 0){
		/* consider code to add player back if player is lost */
		player_addr[num_players] = addr;
		sync_pkt.dest = player_addr[num_players]%256;
		bmac_addr_decode_dest_mac(player_addr[num_players]);
		sync_pkt.player = num_players;
		printf("player number sent: %d\r\n", sync_pkt.player);
		memcpy(&tx_buf, &sync_pkt, type(monome_comm_sync_pkt));
		tx_len = type(monome_comm_sync_pkt);
		while(bmac_tx_pkt( tx_buf, tx_len)!=NRK_OK);
		num_players++; 
		if(num_players == 1)
			state = SELECT;
		printf("Synched new player: player %d\r\n", num_players);
	}
	
}

inline void monome_validate_game(){
	if(select_pkt.players > num_players || select_pkt.players == 0 
	   || (select_pkt.players == 1 && select_pkt.game_type == COOPERATIVE) ){
		monome_comm_send_state(SELECT);
		printf("invalid game\r\n");
	}
	else{
		state = PREGAME;
		tx_buf[2] = state;
		int i;
		for(i = 0; i <select_pkt.players; i++){
			tx_buf[1] = player_addr[i]%256;
			bmac_addr_decode_dest_mac(player_addr[i]);
			while(bmac_tx_pkt( tx_buf, tx_len)!=NRK_OK);
		}
		printf("valid game\r\n");
	
	}
	printf("Gameplay: number of players: | type: %d %d\r\n", select_pkt.players, select_pkt.game_type);
}

inline void monome_comm_send_state(uint8_t state){
	state_pkt.state = state;
	tx_buf[1] = ALL_ADDR%256;
	bmac_addr_decode_dest_mac(ALL_ADDR);
	memcpy(&tx_buf, &state_pkt, type(monome_comm_state_pkt));
	tx_len = type(monome_comm_state_pkt);
	while(bmac_tx_pkt(tx_buf, tx_len)!=NRK_OK);
}

void monome_set_player_initials(uint8_t addr){
	uint8_t i, check;
	if(addr == player_addr[0]){
		memcpy(&player_initials[0], &initials, 3); initials_set[0] = 1;
	} 
	else if(addr == player_addr[1]){
		memcpy(&player_initials[1], &initials, 3); initials_set[1] = 1;
	} 
	else if(addr == player_addr[2]){
		memcpy(&player_initials[2], &initials, 3); initials_set[2] = 1;
	} 
	else if(addr == player_addr[3]){
		memcpy(&player_initials[3], &initials, 3); initials_set[3] = 1;
	} 
	else ;
	check = 0;
	printf("player initials:\r\n");
	for(i = 0; i <select_pkt.players; i++){
		if(initials_set[i]){
			check++;
			printf("player %d: %s\r\n", i, player_initials[i]);
		}
	}
	if(check == select_pkt.players){
		
		tx_buf[2] = GAMEPLAY;
		tx_buf[3] = 1;
		tx_len = 4;
		for(i = 0; i <select_pkt.players; i++){
			tx_buf[1] = player_addr[i]%256;
			bmac_addr_decode_dest_mac(player_addr[i]);
			while(bmac_tx_pkt( tx_buf, tx_len)!=NRK_OK);
		}

		state = GAMEPLAY;
		nrk_time_get(&start_time);
		printf( "Start\r\n" );
	}
	else ;
	check = 0;
	printf("player initials:\r\n");
	for(i = 0; i <select_pkt.players; i++){
		if(initials_set[i]){
			check++;
			printf("player %d: %s\r\n", i, player_initials[i]);
		}
	}
	if(check == select_pkt.players){
		
		tx_buf[2] = GAMEPLAY;
		tx_buf[3] = 1;
		tx_len = 4;
		for(i = 0; i <select_pkt.players; i++){
			tx_buf[1] = player_addr[i]%256;
			bmac_addr_decode_dest_mac(player_addr[i]);
			while(bmac_tx_pkt( tx_buf, tx_len)!=NRK_OK);
		}

		state = GAMEPLAY;
		nrk_time_get(&start_time);
	}
}
